package com.linkgie.galaxyframework.utils;

import java.nio.ByteBuffer;

public abstract class HexUtils {

    /**
     * 含小写字母的字符集合；
     */
    private static final char[] DIGITS_LOWER_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c',
            'd', 'e', 'f' };

    /**
     * 含大写字母的字符集合；
     */
    private static final char[] DIGITS_UPPER_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
            'D', 'E', 'F' };

    public static String encode(long value) {
        return new String(toHexChars(value));
    }

    public static String encode(byte[] data) {
        return new String(toHexChars(data));
    }

    public static String encode(byte[] data, int offset) {
        return new String(toHexChars(data, offset));
    }

    public static String encode(byte[] data, int offset, int len) {
        return new String(toHexChars(data, offset, len));
    }

    public static String encode(byte[] data, boolean lowerCase) {
        return new String(toHexChars(data, 0, lowerCase));
    }

    public static String encode(byte[] data, int offset, boolean lowerCase) {
        return new String(toHexChars(data, offset, lowerCase));
    }

    public static String encode(byte[] data, int offset, int len, boolean lowerCase) {
        return new String(toHexChars(data, offset, len, lowerCase));
    }

    public static String encode(ByteBuffer data) {
        return new String(toToChars(data));
    }

    public static String encode(ByteBuffer data, boolean lowerCase) {
        return new String(toHexChars(data, 0, lowerCase));
    }

    public static byte[] decode(final String data) {
        return decode(data.toCharArray());
    }

    /**
     * 解码为 long 整数；
     * <p>
     * 
     * @param hexString 16进制字符序列；
     *                  <p>
     *                  完整的 64 位整数最长对应 16 个字符；
     *                  <p>
     *                  如果参数长度不足 16 ，则在前面以 '0' 字符补齐；
     *                  <p>
     *                  如果参数长度超过 16 ，则只处理前面 16 个字符，忽略剩余部分；
     * @return
     */
    public static long decodeToLong(final String hexString) {
        return decodeToLong(hexString.toCharArray());
    }

    /**
     * 解码为 long 整数；
     * <p>
     * 
     * @param hexChars 16进制字符序列；
     *                 <p>
     *                 完整的 64 位整数最长对应 16 个字符；
     *                 <p>
     *                 如果参数长度不足 16 ，则在前面以 '0' 字符补齐；
     *                 <p>
     *                 如果参数长度超过 16 ，则只处理前面 16 个字符，忽略剩余部分；
     * @return
     */
    public static long decodeToLong(final char[] hexChars) {
        byte[] bytes = decode(hexChars);
        int n = bytes.length > 8 ? 8 : bytes.length;
        return BinaryUtils.toLong(bytes, 0, n);
    }

    public static byte[] decode(final char[] hexChars) {
        final int len = hexChars.length;

        byte[] out;
        // 是否为奇数；
        boolean oddLen = (len & 0x01) != 0;
        if (oddLen) {
            // 奇数长度；
            out = new byte[(len + 1) >> 1];
        } else {
            // 偶数长度；
            out = new byte[len >> 1];
        }

        int f = 0;
        for (int i = 0, j = 0; j < len; j++) {
            char ch = hexChars[j];
            checkHexChar(ch);

            int digit = Character.digit(ch, 16);

            // 当前处理的16进制字符是否对应为奇数位(从右往左，从低位到高位)；
            boolean odd = ((len - j) & 0x01) != 0;

            // 奇数位字符对应字节的低4位，偶数位字符对应字节的高4位；
            f = f | digit << (odd ? 0 : 4);

            if (odd) {
                // 处理奇数位字符，即完成一个完整字节的处理；
                out[i] = (byte) (f & 0xFF);
                f = 0;
                i++;
            }
        }

        return out;
    }

    private static void checkHexChar(char ch) {
        if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
            return;
        }
        throw new IllegalArgumentException("Illegal hex char [" + ch + "]!");
    }

    /**
     * 将整数转为 16 进制字符；
     * 
     * @param value 整数；
     * @return
     */
    public static String toHexString(long value) {
        return toHexString(value, true);
    }

    /**
     * 将整数转为 16 进制字符；
     * 
     * @param value 整数；
     * @return
     */
    public static String toHexString(int value) {
        return toHexString(value, true);
    }

    /**
     * 将整数转为 16 进制字符；
     * 
     * @param value 整数；
     * @return
     */
    public static char[] toHexChars(long value) {
        return toHexChars(value, true);
    }

    /**
     * 将整数转为 16 进制字符；
     * 
     * @param value 整数；
     * @return
     */
    public static char[] toHexChars(int value) {
        return toHexChars(value, true);
    }

    /**
     * 将整数转为 16 进制字符；
     * 
     * @param value              整数；
     * @param ignoreLeadingZeros 是否忽略正数的前导 0 ；
     * @return
     */
    public static String toHexString(int value, boolean ignoreLeadingZeros) {
        byte[] bytes = BinaryUtils.toBytes(value);
        return toHexString(bytes, ignoreLeadingZeros);
    }

    /**
     * 将整数转为 16 进制字符；
     * 
     * @param value              整数；
     * @param ignoreLeadingZeros 是否忽略正数的前导 0 ；
     * @return
     */
    public static String toHexString(long value, boolean ignoreLeadingZeros) {
        byte[] bytes = BinaryUtils.toBytes(value);
        return toHexString(bytes, ignoreLeadingZeros);
    }

    public static String toHexString(byte[] bytes, boolean ignoreLeadingZeros) {
        return new String(toHexChars(bytes, ignoreLeadingZeros));
    }

    /**
     * 将整数转为 16 进制字符；
     * 
     * @param value              整数；
     * @param ignoreLeadingZeros 是否忽略正数的前导 0 ；
     * @return
     */
    public static char[] toHexChars(int value, boolean ignoreLeadingZeros) {
        byte[] bytes = BinaryUtils.toBytes(value);
        return toHexChars(bytes, ignoreLeadingZeros);
    }

    /**
     * 将整数转为 16 进制字符；
     * 
     * @param value              整数；
     * @param ignoreLeadingZeros 是否忽略正数的前导 0 ；
     * @return
     */
    public static char[] toHexChars(long value, boolean ignoreLeadingZeros) {
        byte[] bytes = BinaryUtils.toBytes(value);
        return toHexChars(bytes, ignoreLeadingZeros);
    }

    public static char[] toHexChars(byte[] bytes, boolean ignoreLeadingZeros) {
        int len = bytes.length;
        int offset = 0;
        if (ignoreLeadingZeros) {
            while (offset < len && bytes[offset] == 0) {
                offset++;
            }
        }
        char[] chars = toHexChars(bytes, offset);
        if (chars.length == 0) {
            return new char[] { DIGITS_UPPER_CHARS[0] };
        }
        if (ignoreLeadingZeros && chars[0] == DIGITS_UPPER_CHARS[0]) {
            char[] newChars = new char[chars.length - 1];
            System.arraycopy(chars, 1, newChars, 0, newChars.length);
            return newChars;
        }
        return chars;
    }

    public static char[] toHexChars(byte[] data) {
        return toHexChars(data, 0, false);
    }

    public static char[] toToChars(ByteBuffer data) {
        return toHexChars(data, 0, false);
    }

    public static char[] toHexChars(byte[] data, int offset) {
        return toHexChars(data, offset, false);
    }

    public static char[] toHexChars(byte[] data, int offset, int len) {
        return toHexChars(data, offset, len, false);
    }

    public static char[] toHexChars(ByteBuffer data, int offset) {
        return toHexChars(data, offset, false);
    }

    public static char[] toHexChars(byte[] data, int offset, boolean lowerCase) {
        return convertToHexChars(data, offset, lowerCase ? DIGITS_LOWER_CHARS : DIGITS_UPPER_CHARS);
    }

    public static char[] toHexChars(byte[] data, int offset, int len, boolean lowerCase) {
        return convertToHexChars(data, offset, len, lowerCase ? DIGITS_LOWER_CHARS : DIGITS_UPPER_CHARS);
    }

    public static char[] toHexChars(ByteBuffer data, int offset, boolean lowerCase) {
        return encode(data, offset, lowerCase ? DIGITS_LOWER_CHARS : DIGITS_UPPER_CHARS);
    }

    /**
     * 编码为 16 进制字符；
     * 
     * @param data   数据；
     * @param offset 起始偏移位置；
     * @param DIGITS 数值符号表；
     * @return 返回 16 进制字符数组，长度是数据数组从起始偏移位置开始剩余长度的 2 倍；
     */
    private static char[] convertToHexChars(byte[] data, int offset, final char[] DIGITS) {
        final int len = data.length;
        return convertToHexChars(data, offset, len, DIGITS);
    }

    private static char[] convertToHexChars(byte[] data, int offset, final int len, final char[] DIGITS) {
        // 16 进制字符数组长度是字节数组长度的 2 倍；
        final char[] hexChars = new char[(len - offset) << 1];

        for (int i = offset, j = 0; i < len; i++) {
            // 高 4 位的值；
            hexChars[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
            // 低 4 位的值；
            hexChars[j++] = DIGITS[0x0F & data[i]];
        }
        return hexChars;
    }

    private static char[] encode(ByteBuffer byteBuffer, int offset, char[] hexDigits) {
        return convertToHexChars(getBytes(byteBuffer), offset, hexDigits);
    }

    public static String encode(int value) {
        return new String(toHexChars(value));
    }

    private static byte[] getBytes(ByteBuffer byteBuffer) {
        if (byteBuffer.hasArray()) {
            return byteBuffer.array();
        }
        final byte[] byteArray = new byte[byteBuffer.remaining()];
        byteBuffer.get(byteArray);
        return byteArray;
    }

}
